/**
 * ****************************************************************************
 * Project: RoboBridge
 * ----------------------------------------------------------------------------
 * -------------------------- [c]2012 - KrawallMann ---------------------------
 * ---------------------- http://www.krawallmann.de.tc/ -----------------------
 * ****************************************************************************
 * File: UpdateState.java Package:
 * tc.de.krawallmann.robobridge.communication.protocol Version: 1.0 Author(s):
 * KrawallMann
 * ****************************************************************************
 * Description:
 * ****************************************************************************
 */
package tc.de.krawallmann.robobridge.communication.protocol;

import java.util.logging.Level;

import tc.de.krawallmann.robobridge.RoboBridge;
import tc.de.krawallmann.robobridge.communication.AbstractRemoteCommunicationDevice;
import tc.de.krawallmann.robobridge.communication.Command;
import tc.de.krawallmann.robobridge.communication.CommandType;
import tc.de.krawallmann.robobridge.util.Log;

/**
 * The Class UpdateState updates the robot's hardware and sends the Commands.
 * 
 * @author KrawallMann
 * @version 1.0
 */
public class UpdateState extends AbstractProtocolState<RoboBridgeProtocol> {

	/** The bridge. */
	private transient final RoboBridge<? extends AbstractRemoteCommunicationDevice> bridge;

	/** The first loop done. */
	private transient boolean firstLoopDone;

	/** The last ping sent on. */
	private transient long lastPingSentOn;

	/** The ping. */
	private transient long ping = -1;

	/** The ping sent. */
	private transient boolean pingSent;

	/**
	 * Instantiates a new update state.
	 * 
	 * @param protocol
	 *            the protocol
	 * @param bridge
	 *            the bridge
	 */
	public UpdateState(final RoboBridgeProtocol protocol,
			final RoboBridge<? extends AbstractRemoteCommunicationDevice> bridge) {
		super(protocol);
		this.bridge = bridge;
	}

	/**
	 * Gets the ping.
	 * 
	 * @return the ping
	 */
	public long getPing() {
		return ping;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.robobridge.communication.protocol.AbstractProtocolState
	 * #isComplete()
	 */
	@Override
	public boolean isComplete() {
		return bridge.getProgram() == null || bridge.getProgram().isTerminationRequested();
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.robobridge.communication.protocol.AbstractProtocolState
	 * #onCommandReceived(basepackage.Command)
	 */
	@Override
	public boolean onCommandReceived(final Command command) {
		if (super.onCommandReceived(command)) {
			return true;
		}
		if (command.getType() == CommandType.UPDATE_DEVICE_DATA) {
			// Forward it to the robot!
			bridge.getRobot().queueIncomingingCommand(command);
			return true;
		} else if (command.getType() == CommandType.PING) {
			ping = System.currentTimeMillis() - lastPingSentOn;
			pingSent = false;
			bridge.getProtocol().setPing(ping);
			return true;
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.robobridge.communication.protocol.AbstractProtocolState
	 * #update()
	 */
	@Override
	public void update() {
		bridge.getRobot().processIncoming();
		bridge.getRobot().updateHardware();
		if (System.currentTimeMillis() - lastPingSentOn >= 1000 && !pingSent) {
			try {
				protocol.sendCommand(new Command(CommandType.PING, false));
			} catch (final InterruptedException exc) {
				Log.log(Level.WARNING, getClass(), "update",
						"Exception while sending PING request - {0} !", exc.getMessage());
			}
			pingSent = true;
			lastPingSentOn = System.currentTimeMillis();
		}

		if (bridge.getProgram() != null && bridge.isExecutingProgram()) {
			if (!firstLoopDone) {
				bridge.getProgram().onFirstLoop();
				firstLoopDone = true;
			}
			bridge.getProgram().update();
		}

		Command command = bridge.getRobot().pollOutgoingCommand();
		while (command != null) {
			try {
				protocol.sendCommand(command);
			} catch (final InterruptedException exc) {
				Log.log(Level.WARNING, getClass(), "update",
						"Exception while sending command {0} - {1} !", command, exc.getMessage());
			}
			command = bridge.getRobot().pollOutgoingCommand();
		}
	}
}
